#pragma once
/*
 * rpc stream.  
 */
#include "asio/detail/noncopyable.hpp"
#include "asio/asio.hpp"
#include <memory>
#include <iostream>

namespace anet {
    namespace rpc_codec {
        class streambuf_ex : public asio::streambuf {
        protected:
            pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode mode) {
                return _seekoff(off, way, mode);
            }

            pos_type seekpos(pos_type off, std::ios_base::openmode mode) {
                return _seekoff(off, std::ios_base::cur, mode);
            }

        private:
            pos_type _seekoff(pos_type off, std::ios_base::seekdir way, 
                std::ios_base::openmode mode) {
                pos_type ret = pos_type(-1);
                if (mode & std::ios_base::in) {
                    if (mode & std::ios_base::out) {
                        return ret;
                    }
                    if (pptr() && egptr() < pptr()) {
                        setg(eback(), gptr(), pptr());
                    }
                    pos_type offset = off;
                    if (way == std::ios_base::cur) {
                        offset += gptr() - eback();
                    } else if (way == std::ios_base::end) {
                        offset += egptr() - eback();
                    }

                    if (offset >= 0 && egptr() - eback() >= offset) {
                        setg(eback(), eback() + offset, egptr());
                        ret = offset;
                    }
                } else if (mode & std::ios_base::out) {
                    if (pptr() && egptr() < pptr()) {
                        setg(eback(), gptr(), pptr());
                    }

                    pos_type offset = off;
                    if (way == std::ios_base::cur) {
                        offset += pptr() - eback();
                    } else if (way == std::ios_base::end) {
                        offset += egptr() - eback();
                    }
                    if (offset >= 0) {
                        setp(eback(), epptr());
                        pbump(offset);
                        ret = offset;
                    }
                }
                return ret;
            }
        };

        class rpc_stream : private asio::noncopyable {
        public:
            rpc_stream() : m_stream(&m_buf) {
            }
            ~rpc_stream() {
            }

        public:
            std::istream &read_stream() {
                return m_stream; 
            }
            std::ostream &write_stream() { 
                return m_stream; 
            }
            streambuf_ex &buf() { 
                return m_buf; 
            }
            void reset() {
				write_stream().seekp(0, std::ios_base::beg);
				read_stream().seekg(0, std::ios_base::beg);
            }

            // get char* string.
            char* c_str() {
                return (char*)const_cast<void*>(asio::buffer_cast<const void*>(buf().data()));
            }

        private:
            streambuf_ex m_buf;
            std::iostream m_stream;
        };
    }
}